package threadsafe2;
/*
银行账户
 */
public class Account {
    private String actno;
    private double balance;
    Object obj = new Object();

    public Account() {
    }

    public Account(String actno, double balance) {
        this.actno = actno;
        this.balance = balance;
    }

    public String getActno() {
        return actno;
    }

    public void setActno(String actno) {
        this.actno = actno;
    }

    public double getBalance() {
        return balance;
    }

    public void setBalance(double balance) {
        this.balance = balance;
    }

    //取款的方法
    public void withdraw(double money) {
        /*
        public void synchronized withdraw(double money)
        在实例方法上可以使用synchronized
            synchronized出现在实例方法上，一定锁的是this，所以这种方式不灵活
            另外还有一个缺点：synchronized出现在实例方法上，表示整个方法都需要同步，可能会无故扩大同步范围，导致程序的执行效率降低

            synchronize优点：代码少了，节俭
            如果共享的对象就是this，并且需要同步的代码块是整个方法体，建议使用这个方式

         */

        //以下这几行代码必须是线程排队的，不能并发
        //一个线程把这里的代码全部执行结束之后，另一个线程才能进来
        /*
        线程同步机制的语法：
            synchronized(){

            }
            synchronized后面小括号中传的这个数据非常关键
            这个数据必须是多线程共享的数据，才能达到多线程排队

            ()中写什么？
                看想让哪些线程同步
                假设t1 t2 t3 t4 t5，5个线程
                只希望t1 t2 t3排队，t4 t5不排队，怎么办？
                所以在()中写一个t1 t2 t3共享的对象，而这个对象对t4 t5不共享

         */
        /*这里的共享对象是：账户对象
        账户对象是共享的，this就是账户对象

        在java语言中，任何一个对象都有一把锁，其实这把锁就是标记
        以下代码的执行原理？
            1、假设t1和t2线程并发，开始执行以下代码的时候，肯定有个先有个后
            2、假设t1先执行了，遇到了synchronized，这个时候自动找“后面共享对象”的对象锁，找到之后，并占有这把锁，然后执行同步代码块的内容，在
            程序执行过程中一直都是占有这把锁的。直到同步代码块结束，这把锁才会释放。
            3、假设t1已经占有这把锁，此时t2也遇到synchronized关键字，也会去占有后面共享对象的这把锁，结果这把锁被t1占有，t2只能在同步代码块
            外面等t1的结束，直到t1把同步代码块执行结束了，t1会归还这把锁，此时t2等到了这把锁，然后t2占有这把锁之后，进入同步代码块执行程序。

            这样就达到了线程排队执行
            注意：共享对象一定要选好，这个共享对象一定是你需要排队执行的这些线程对象所共享的。
        */

        //synchronized (this) {                       //this同一对象的线程需要排队
        //synchronized (obj) {                        //obj也是一个共享对象
        synchronized ("abx") {                       //"abx"在字符串常量池中，所有线程都需要排队
            double before = this.getBalance();
            double after = before - money;

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            this.setBalance(after);
        }
    }

}
